package edu.put.sk.nrpp;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.w3c.dom.ranges.RangeException;

public class Bit {
	private int value;
	public Bit(int b) throws RangeException{
		if(b!=0&&b!=1)
			throw new RangeException((short) b, "Accepted value 0 or 1"); //$NON-NLS-1$
		setValue(b);
	}
	
	public Bit(boolean b){
		if(b)
			value=1;
		else
			value=0;
	}
	public void setValue(int value) {
		this.value = value;
	}
	public int getValue() {
		return value;
	}
	
	public static Bit[] parseByte(byte bytes){
		byte[] b=new byte[1];
		b[0]=bytes;
		return Bit.parseByte(b);
	}
	
	public static Bit[] parseByte(byte[] bytes){
		Bit[] ret=new Bit[bytes.length*8];
		int i=0;
		for(byte b : bytes){
			for(int a=7; a>=0; a--){
				//	System.out.print((int)(b & (1 << a)) != 0);
				ret[i]=new Bit(((b & (1 << a)) != 0));
				i++;
			}
		}
		
		
		return ret;
		
	}
	
	@Override
	public String toString(){
		return new Integer(getValue()).toString();
	}
	
	public static int toTwosComplement(Bit[] bits) throws IndexOutOfBoundsException{
		if(bits.length%8!=0){
			throw new IndexOutOfBoundsException();
		}
		
		if(bits[0].getValue()==1){
			//minus
			Bit[] d=new Bit[bits.length];
			int i=0;
			for(Bit b:bits){
				d[i++]=(b.getValue()==1)?new Bit(0):new Bit(1);
			}
			return Bit.toInt(d)*(-1)-1;
		}
		
		return Bit.toInt(bits);
		
	}
	
	public static int toOnesComplement(Bit[] bits) throws IndexOutOfBoundsException{
		if(bits.length%8!=0){
			throw new IndexOutOfBoundsException();
		}
		
		if(bits[0].getValue()==1){
			//minus
			Bit[] d=new Bit[bits.length];
			int i=0;
			for(Bit b:bits){
				d[i++]=(b.getValue()==1)?new Bit(0):new Bit(1);
			}
			return Bit.toInt(d)*(-1);
		}
		return Bit.toInt(bits);
		
	}
	
	
	public static Bit[] AddUsingOnesComplement(Bit[] numberA, Bit[] numberB) throws IndexOutOfBoundsException{
		if(numberA.length%8!=0||numberB.length%8!=0){
			throw new IndexOutOfBoundsException();
		}
		Bit[] a, b;
		
		if(numberA.length>=numberB.length){
			a=numberA;
			b=numberB;
		}
		else{
			b=numberA;
			a=numberB;
		}
		
		Bit[] ret=new Bit[a.length];
		int add=0;
		
			for(int i=b.length-1; i>=0; i--){
				int d=a[i].getValue()+b[i].getValue()+add;
				ret[i]=new Bit(d%2);
				if(d>0&&d%2==0)
					add=d-d%2-1;
				else
					add=d-d%2;
			}
		int i=ret.length-1;
		while(add!=0){
			int d=ret[i].getValue()+add;
			ret[i]=new Bit(d%2);
			
			if(d>0&&d%2==0)
				add=d-d%2-1;
			else
				add=d-d%2;
			
			if(i==0)
				i=ret.length-1;
			else
				i--;
		}
		return ret;
	}
	
	public static int toInt(Bit[]bits){
		int r=0;
		int i=0, length=bits.length;
		for(Bit bit:bits){
			int x=(int)Math.pow(2, (length-1-i)) * (bit.getValue());
			r+=x;
			//1101
			i++;
		}
		return r;
	}
	
	public static Bit[] cut(Bit[] bit, int a, int b){
		try{
		Bit[] r=new Bit[Math.min((b-a+1), bit.length)];
		for(int i=0; i<Math.min((b-a+1), bit.length); i++){
			r[i]=bit[a+i];
		}
		return r;
		}
		catch(Exception e) {
			return null;
		}
	}
	
	public static byte toByte(Bit[] bits){
		
		return (byte) Bit.toInt(bits);
	
	}
	public static byte[] toBytes(Bit[] bits){
		
		int size=bits.length/8;
		byte[]res=new byte[size];
		for(int i=0; i<size; i++){
			res[i]=Bit.toByte(Bit.cut(bits, (i*8), ((i+1)*8)-1));
		}
		return res;
	}
	public static InetAddress toInetAddres(Bit[] bits) throws UnknownHostException{
		byte[] address=new byte[4];
		address[0]=Bit.toByte(Bit.cut(bits, 0, 7));
		address[1]=Bit.toByte(Bit.cut(bits, 8, 15));
		address[2]=Bit.toByte(Bit.cut(bits, 16, 23));
		address[3]=Bit.toByte(Bit.cut(bits, 24, 31));		
		return InetAddress.getByAddress(address);	
	}
}
